#define _DEF_MEMORY_USAGE
#include "memrec.h"

#ifndef _linux_
#include "windows.h"
#endif

#include <vector>

void DummyFunc()
{
	int a = 0; 
	a++;
}

tMemoryUsage::tMemoryUsage():
ptr(NULL), size(0)
{
}

tMemoryUsage::tMemoryUsage(const tMemoryUsage& MU)
{
	ptr = MU.ptr;
	size = MU.size;
	line = MU.line;
	if (MU.file)
#ifdef _linux_
		strncpy(file, MU.file, 256);
#else
		strncpy_s(file, 256, MU.file, 256);
#endif
	if (MU.usage == NULL)
	{
#ifdef _linux_
		strcpy(usage, "Unknow");
#else
		strcpy_s(usage, 32, "Unknow");
#endif
	}
	else
#ifdef _linux_
		strncpy(usage, MU.usage, 32);
#else
		strncpy_s(usage, 32, MU.usage, 32);
#endif
}

tMemoryUsage & tMemoryUsage::operator= (const tMemoryUsage & MU)
{
	ptr = MU.ptr;
	size = MU.size;
	line = MU.line;
	if (MU.file)
#ifdef _linux_
		strncpy(file, MU.file, 256);
#else
		strncpy_s(file, 256, MU.file, 256);
#endif
	if (MU.usage == NULL)
	{
#ifdef _linux_
		strcpy(usage, "Unknow");
#else
		strcpy_s(usage, 32, "Unknow");
#endif		
	}
	else
#ifdef _linux_
		strncpy(usage, MU.usage, 32);
#else
		strncpy_s(usage, 32, MU.usage, 32);
#endif
	return *this;
}

tMemoryUsage::tMemoryUsage(void* p, size_t s, const char* f, const char* u, int l)
{
	ptr = p; size = s; line = l;
#ifdef _linux_
		strncpy(file, f, 256);
#else
		strncpy_s(file, 256, f, 256);
#endif
	if (u == NULL)
#ifdef _linux_
		strcpy(usage, "Unknown");
#else
		strcpy_s(usage, 32, "Unknown");
#endif	
	else
#ifdef _linux_
		strncpy(usage, u, 32);
#else
		strncpy_s(usage, 32, u, 32);
#endif
}

tMemoryUsageElement::tMemoryUsageElement(tMemoryUsage & MU):
	tMemoryUsage(MU), Next(0)
{
		
}
	
tMemoryUsageElement& tMemoryUsageElement::operator= (const tMemoryUsage & MU)
{
	tMemoryUsage::operator=(MU);
	return *this;
}

void* tMemoryUsageElement::operator new (size_t size)
{
	return malloc(size);
}
void  tMemoryUsageElement::operator delete(void * p)
{
	free(p);
}

tMemoryRecords::tMemoryRecords(const char * filename)
{
	ListHead = NULL;
	memset(EmptyMemoryUsageEl, 0, sizeof(EmptyMemoryUsageEl));
#ifndef _linux_
	char file[256];
	HMODULE hModule = NULL;
    GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 
			(LPCSTR)&DummyFunc,
			&hModule);
	GetModuleFileNameA(hModule, file, 256);
	char * basefile = file + strlen(file) - 1;
	while (*basefile != '\\' && basefile > file)
		basefile--;
	basefile++;
	sprintf_s(m_OutFileName, 256, "memleak_%u_%s.log", GetCurrentProcessId(), basefile);
#else
	sprintf(m_OutFileName, "//tmp//%d_memleak.log", getpid());
#endif
}

tMemoryRecords::~tMemoryRecords()
{
    if (ListHead != NULL)
    {
#ifdef _DEBUG
		DumpLeak(m_OutFileName);
#endif //_DEBUG
    }
	tMemoryUsageElement * ele = ListHead;
	tMemoryUsageElement * eledel = NULL;
	while (ele != NULL)
	{
		eledel = ele;
		ele = eledel->Next;
		delete eledel;
	}
	for (unsigned int i = 0; i < MAX_REC_SIZE; i++)
	{
		if (EmptyMemoryUsageEl[i] != 0)
		{
			delete EmptyMemoryUsageEl[i];
			EmptyMemoryUsageEl[i] = 0;
		}
	}
}

void tMemoryRecords::AddToLink(tMemoryUsageElement * pMU)
{
	pMU->Next = ListHead;
	ListHead = pMU;
}
tMemoryUsageElement * tMemoryRecords::AssignToEmptyElement(const tMemoryUsage * MU)
{
	for (int i = 0; i < MAX_REC_SIZE; i++)
	{
		if (EmptyMemoryUsageEl[i] != 0)
		{
			*(EmptyMemoryUsageEl[i]) = *MU;
			tMemoryUsageElement * Ret = EmptyMemoryUsageEl[i];
			EmptyMemoryUsageEl[i] = 0;
			return Ret;
		}
	}
	return NULL;
}

void tMemoryRecords::AddRec(tMemoryUsage MU)
{
	AUTO_MUTEX_2 lock(Mutex);
	tMemoryUsageElement * pEmptyEle = AssignToEmptyElement(&MU);
	if (pEmptyEle)
	{
		AddToLink(pEmptyEle);		
	}
	else
	{
		// allocate new one.
		tMemoryUsageElement * NewEle = new tMemoryUsageElement(MU);
		if (NewEle)
		{
			AddToLink(NewEle);
		}
	}
}

tMemoryUsageElement * tMemoryRecords::RemoveEleInList(void * ptr)
{
	tMemoryUsageElement * pCur = ListHead;
	tMemoryUsageElement * pPrev = 0;
	while (pCur != NULL)
	{
		if (pCur->ptr == ptr)
		{
			// rm
			if (pPrev == 0)
			{
				ListHead = pCur->Next;
			}
			else
			{
				pPrev->Next = pCur->Next;
			}
			return pCur;
		}
		else
		{
			pPrev = pCur;
			pCur = pCur->Next;
		}
	}
	return NULL;
}

void tMemoryRecords::AddEmpty(tMemoryUsageElement * pEmpty)
{
	for (int i = 0; i < MAX_REC_SIZE; i++)
	{
		if (EmptyMemoryUsageEl[i] == 0)
		{
			EmptyMemoryUsageEl[i] = pEmpty;
			return;
		}
	}
	// free the memory. no space to records them.
	delete pEmpty;
}

void tMemoryRecords::RemRec(void * p)
{
	AUTO_MUTEX_2 lock(Mutex);
	tMemoryUsageElement * pRm = RemoveEleInList(p);
	if (pRm)
	{
		// add to empty list.
		AddEmpty(pRm);
	}
	else
	{
		//What we need to do when no item found?
	}
}

void tMemoryRecords::DumpLeak(const char * filepath)
{
	AUTO_MUTEX_2 lock(Mutex);
#ifdef _linux_
	FILE * fd = fopen(filepath, "w");
#else
	FILE *fd = NULL;
	fopen_s(&fd, filepath, "a");
#endif
	if (fd == 0)
		return;
	tMemoryUsageElement * pCur = ListHead;
	while (pCur != NULL)
	{
		fprintf(fd, "0x%p, src : %s, line %d, usage : %s\n",  
					pCur->ptr, pCur->file, pCur->line, pCur->usage);
		unsigned char * d = (unsigned char *)pCur->ptr;
		for (int i = 0; i < 8; i++)
		{
		    fprintf(fd, "%x ", d[i]);
		}
		fprintf(fd, "\n");
		pCur = pCur->Next;
	}
	fclose(fd);
}

#ifdef _DEBUG_MEMORY_LEAK
void * operator new (size_t size, const char* file, int line, const char * usage)
{
	void * p = ::operator new(size);
	if (p != NULL)
		gMemory.AddRec(tMemoryUsage(p, size, file, usage, line));
	/*
	char debugstr[1024];
	sprintf_s(debugstr, 1024, "Allocator:%p-%p: %s(%d)\n", p, (char*)p+size, file, line);
	OutputDebugStringA(debugstr);
	*/
	return p;
}

void * operator new[] (size_t size, const char* file, int line, const char * usage)
{
	void * p = ::operator new[](size);
	if (p != NULL)
		gMemory.AddRec(tMemoryUsage(p, size, file, usage, line));
	/*
	char debugstr[1024];
	sprintf_s(debugstr, 1024, "Allocator:%p-%p: %s(%d)\n", p, (char*)p+size, file, line);
	OutputDebugStringA(debugstr);
	*/
	return p;
}

void operator delete (void *p, const char* file, int line, const char * usage)
{
	free(p);
	gMemory.RemRec(p);
}

void operator delete[] (void *p, const char* file, int line, const char * usage)
{
	free(p);
	gMemory.RemRec(p);
}

void operator delete (void *p)
{
	free(p);
	gMemory.RemRec(p);
}

void operator delete[] (void *p)
{
	free(p); 
	gMemory.RemRec(p);
}
#endif
